home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
pctj8409.arc
/
PASCAL.ASM
< prev
next >
Wrap
Assembly Source File
|
1986-09-14
|
12KB
|
279 lines
.XLIST
;***********************************************************************;
; ;
;PASCAL.MAC ;
;(C)(P)Copyright 1983 by Eric S. Raymond ;
; These macros are useful for generating ASM-86 routines that ;
; do stack-based argument handling, and can thus be treated like ;
; Pascal-86 or BASIC routines once declared in an interface or ;
; EXTERNed to. Note that they must be PUBLICed and live in a ;
; SEGMENT PUBLIC PARA 'CODE' to be accessible to the linker. ;
; These macros should work correctly with any compiler that ;
; a) only requires BP to be saved during subroutine calls, and ;
; b) pushes arguments left to right (last given is last pushed). ;
; ;
;I) General service routines ;
; To generate a routine with a given profile of value and VAR ;
; args and a given service routine, write the following; ;
; ;
;[name] PROC ;
; PROLOG <arg1, arg2,...argn>, tr ;
; [service code] ;
; EPILOG reg ;
;[name] ENDP ;
; ;
; If A stands for an accumulator name, argk may have the form ;
; A -- argument k is a value argument to be moved to A ;
; #A -- argument k is a CONST argument (input only) ;
; *A -- argument k is a VAR argument (output only) ;
; @A -- argument k is a VAR argument (input and output) ;
; For these four cases, PROLOG fetches argument k to A and EPILOG ;
; automatically stores A to it (if it's VAR). ;
; If argk has none of these forms, it is simply equated to k ;
; in the assembler's symbol tables, and can be used with GETVAL, ;
; GETVAR, and SETVAR to give the effect of named arguments. ;
; If a register reg is specified, that register will be loaded ;
; into AL or AX (depending on length) just before exit and Pascal ;
; will see it as a return value. If reg is CY, AL will be loaded ;
; with 1 or 0 as the carry flag is on or off just before exit. ;
; Since the stack loads don't change the state of the flags, AL ;
; may pass out a VAR argument as usual before this. ;
; By default SI is used as a scratch register for locations of ;
; VAR arguments. This will lose if a VAR or CONST argument occurs ;
; after SI in the argument list. The optional second argument tr ;
; of PROLOG may be used to specify another scratch register; it ;
; may be SI, DI or BX and is passed to GETVAR and SETVAR. ;
; ;
;II) GETVAL, GETVAR and SETVAR ;
; The macros GETVAL reg, k and GETVAR reg, k will load a register ;
; from the kth value or location argument respectively. The macro ;
; SETVAR k, reg will store REG to the kth by-location argument. ;
; GETVAR and SETVAR take an optional third argument which sets ;
; the location scratch register as described above. ;
; ;
;III) WITH1, WITH2 ;
; The macros WITH1 and WITH2 allow any instruction that will take ;
; the appropriate addressing mode to be applied to two operands, ;
; one of which is a stacked argument represented by its name. For ;
; example: ;
; WITH2 CMP, AX, FOO ;
; expands to CMP AX, [BP+X*2] where X is the stack offset of the ;
; argument named by FOO. WITH1 does this to its second argument, ;
; so WITH1 CMP, FOO, AX would be equivalent to the above. ;
; ;
;IV) SYS function calls ;
; A macro has been included for generating interfaces to the ;
; PC's BIOS and DOS interrupt servers. With this macro, ;
; ;
; SYS name, int, func, <arg1, arg2,... argn>, reg, tr ;
; ;
; generates source for a Pascal-accessible routine that executes ;
; PROLOG, does an INT (int) with (func) in AH, and then does ;
; EPILOG. Arguments in the bracketed list are loaded and returned ;
; as in PROLOG/EPILOG. Optional args reg and tr are as above. ;
; SYS will PUBLIC the generated function, though this won't ;
; be obvious in an .XALL listing since PUBLIC generates no code. ;
; ;
;***********************************************************************;
.XCREF
INTFUN MACRO INTN, FUNCN ;;Call a BIOS function
MOV AH, FUNCN ;; with given function number
INT INTN ;; and given interrupt
ENDM
ACOUNT MACRO ARG ;;Counts arguments
IRP REG, <AL,AH,BL,BH,CL,CH,DL,DH,AX,BX,CX,DX,SI,DI>
IFIDN <ARG>, <REG> ;;If it's a value argument
INCT = INCT + 1 ;; bump the input argument count
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
IFIDN <ARG>, <#®> ;;If it's a CONST argument
INCT = INCT + 1 ;; bump the input argument count
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
IFIDN <ARG>, <*®> ;;If it's an output-only VAR arg
OUTCT = OUTCT + 1 ;; bump the output argument count
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
IFIDN <ARG>, <@®> ;;If it's a VAR argument
IOCT = IOCT + 1 ;; bump the input/output arg count
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
LACT = LACT + 1 ;;It's a named argument
ENDM ;;End of IRP
ENDM ;;End of ACOUNT
GETVAL MACRO Z, N ;;Load Nth value arg to Z
MOV Z, [BP+2*(ARITY-N)+6] ;;Move parameter to register
ENDM ;;End of GETVAL
GETVAR MACRO Z, N, T ;;Load Z to Nth VAR argument
IFB <T> ;;If no transport reg specified
MOV SI, [BP+2*(ARITY-N)+6] ;; fetch parameter address
MOV Z, [SI] ;; and load to where it points
ELSE ;;Else transport register given
MOV T, [BP+2*(ARITY-N)+6] ;; fetch parameter address
MOV Z, [T] ;; and load to where it points
ENDM ;;End of GETVAR
INARG MACRO FML, K, TR ;;Generate stack fetch for arg K
;;First check the null case
IFB <FML> ;;If blank,
%OUT K : skipped ;; note: stack slot is skipped
EXITM ;; and exit INARG
ENDIF ;;End of blank check
;;Now look for a matching register argument
ISR = 0 ;;No register match yet found
IRP REG, <AL,AH,BL,BH,CL,CH,DL,DH,AX,BX,CX,DX,SI,DI,SP>
;;Handle value arguments
IFIDN <FML>, <REG> ;;If it's a value argument
ISR = 1 ;; note that it matched
GETVAL REG, K ;; fetch it
%OUT K : VAL REG ;; and show it in the listing
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
;;Handle CONST arguments
IFIDN <FML>, <#®> ;;If it's CONST
ISR = 1 ;; note that it matched
GETVAR REG, K, TR ;; fetch Kth location arg
%OUT K : CONST REG ;; and show it in the listing
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
;;Handle two-way VAR arguments
IFIDN <FML>, <@®> ;;If it's VAR
ISR = 1 ;; note that it matched
GETVAR REG, K, TR ;; fetch Kth location arg
%OUT K : VAR REG ;; and show it in the listing
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
;;Handle output VAR arguments
IFIDN <FML>, <*®> ;;If it's an output-only arg
ISR = 1 ;; note that it matched
%OUT K : VAR REG (out only) ;; show it in the listing
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
ENDM ;;End of IRP
;;If no matching register arg, equate name to arg number
IFE ISR ;;No register argument match
FML = K ;;Equate name to arg value
%OUT K : FML = K ;;Report the action
ENDIF ;;End of symbol check
ENDM ;;End of INARG
PROLOG MACRO ARGS, TR ;;Process input arguments
.XCREF ;;Don't need generated code to be CREFed
ARITY = 0 ;;Start with 0 total arguments
INCT = 0 ;;Start with 0 input args
OUTCT = 0 ;;Start with 0 output args
IOCT = 0 ;;Start with 0 input/output args
LACT = 0 ;;Start with 0 named arguments
IRP X, <ARGS> ;;Count the flavors of arguments
ARITY = ARITY + 1 ;; ARITY counts all four kinds
ACOUNT X ;; Now count the individual kinds
ENDM ;;End of argument count loop
PUSH BP ;;Save that frame pointer
IF INCT+IOCT+LACT ;;If there are input args
MOV BP, SP ;; set up for stack access
C = 0 ;; initialize argument count
IRP X, <ARGS> ;; and loop through the argument list
C = C + 1 ;; using C to count from 1 to ARITY
INARG X, %C, TR ;; generating stack fetches as we go
ENDM ;;End of argument-list processing loop
ENDIF ;;End of 'if there are input args'
EPILOG MACRO REG ;;Generate EPILOG macro
ENDF <ARGS>, REG, TR ;;Have it call ENDF with ARGLIST
ENDM ;;End of generated macro
.CREF ;;Restore CREF
ENDM ;;End of PROLOG
SETVAR MACRO N, Z, T ;;Store Z to Nth VAR argument
IFB <T> ;;If no transport reg specified
MOV SI, [BP+2*(ARITY-N)+6] ;; fetch parameter address
MOV [SI], Z ;; and load to where it points
ELSE ;;Else transport register given
MOV T, [BP+2*(ARITY-N)+6] ;; fetch parameter address
MOV [T], Z ;; and load to where it points
ENDM ;;End of SETVAR
OUTARG MACRO K, FML, TR ;;Gen stack load for Kth VAR arg
IFNB <FML> ;;If blank, do nothing
IRP REG, <AL,AH,AX,BL,BH,BX,CL,CH,CX,DL,DH,DX>
IFIDN <FML>, <*®> ;;If it's an output-only arg
SETVAR K, REG, TR ;; store to its location
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
IFIDN <FML>, <@®> ;;If it's a VAR argument
SETVAR K, REG, TR ;; store to its location
EXITM ;; then exit the IRP
ENDIF ;; else continue list check
ENDM ;;End of IRP
ENDIF ;;Skip if <ARG> is blank
ENDM ;;End of OUTARG
MOVACC MACRO REG ;;Gen code to move REG to AL/AX
%OUT REG value returned ;;Show it in the listing
IFDIF <RETREG>, <AX> ;;Skip the rigamarole if it's AX
IFDIF <RETREG>, <AL> ;; likewise if it's AL
IRP X, <AH,CH,DH,BH,CL,DL,BL> ;;Try 8-bit registers
IFIDN <REG>, <X> ;;If REG is one
MOV AL, REG ;; then move it to AL
EXITM ;; then exit the IRP
ENDIF ;; else try the next one
ENDM ;;End of 8-bit register IRP
IRP X, <CX,DX,BX,SI,DI,BP,SP> ;;Try 16-bit registers
IFIDN <REG>, <X> ;;If REG is one
MOV AX, REG ;; then move it to AX
EXITM ;; then exit the IRP
ENDIF ;; else try the next one
ENDM ;;End of 16-bit register IRP
IFIDN <REG>, <CY> ;;Should carry flag be returned?
XOR AL, AL ;; If so, zero AL
RCR AL ;; and rotate in the carry bit
ENDIF ;;End of carry bit processing
ENDIF ;;Skip here if RETREG was AL
ENDIF ;;Skip here if RETREG was AX
ENDM ;;End of MOVACC
ENDF MACRO ARGS,RETREG,TR ;;Gen stack loads and RET for routine
.XCREF ;;Don't need cross-referencing here
IF OUTCT + IOCT ;;If there are output arguments
MOV BP, SP ;; set up for stack access
C = 0 ;; initialize arg ctr
IRP X, <ARGS> ;; and loop through it,
C = C + 1 ;; using C to count from 1 to ARITY
OUTARG %C, X, TR ;; generating stack loads as we go
ENDM ;;End of argument list processing loop
ENDIF ;;Now handle the return
IFNB <RETREG> ;;If a return reg has been specified
MOVACC RETREG ;; generate code to move it to AL or AX
ENDIF ;;skip here if RETREG was blank
POP BP ;;Restore frame ptr
RET 2*ARITY ;;Clean args off stack
%OUT ;;Make spacing blank line in listing
.CREF ;;Restore cross-ref'ing for next routine
ENDM ;;End of ENDF
WITH1 MACRO OP, K, ARG ;;Apply OP to
OP [BP+2*(ARITY-K)+6], ARG ;;Kth stack entry & ARG
ENDM ;;End of WITH1
WITH2 MACRO OP, ARG, K ;;Apply OP to
OP ARG, [BP+2*(ARITY-K)+6] ;;ARG & Kth stack entry
ENDM ;;End of WITH2
SYS MACRO NAME, INTN, FUNCN, ARGS, REG ;;Gen SYS call interface
%OUT NAME ;;Let user know we're here
PUBLIC NAME ;;Make sure proc is accessible to Pascal
NAME PROC FAR ;;Start of generated procedure
PROLOG <ARGS> ;;Count & fetch input arguments
INTFUN INTN, FUNCN ;;Call the interrupt function
EPILOG REG ;;Call the macro generated by PROLOG
NAME ENDP ;;End of generated procedure
ENDM ;;End of SYS
.LALL
.CREF
.LIST